import { Plane } from "@babylonjs/core/Maths/math.plane";
import { TransformNode } from "@babylonjs/core/Meshes/transformNode";
import { Vector3 } from "@babylonjs/core/Maths/math.vector";
import { AbstractDragLimit } from "./abstract-drag-limit";
import { IDragProgressEventData, IDragEventData } from "../config/abstract-drag-config";
import { Calculation } from "../../tool/calculation";
import { BabylonContainer } from "../../base/babylon-container";

/**
 * 拖动中的回调函数
 * @param target 对象
 * @param manual 是否手动
 */
interface IProgressCallback {
    (target: TransformNode, manual: boolean): void
}

/**
 * 自由拖动（无约束）
 * 分为两种模式：
 *      1.在拖动开始时设置拖动平面，拖动范围为垂直于视线的平面（默认）；
 *      2.在拖动过程中更新拖动平面，拖动范围为以相机为中心的曲面（近似球面）；
 */
class DragNoneLimit extends AbstractDragLimit {

    private camVec: Vector3 = new Vector3();
    private camDirN: Vector3 = new Vector3();
    private plane: Plane = new Plane(0, 0, 0, 1);

    /**
     * 是否动态更新拖动平面
     */
    public updateDragPlane: boolean = false;

    /**
     * 获取约束类型标识
     */
    public get limitType(): string {
        return 'NoneLimit';
    }

    /**
     * 设置拖动条件
     * @param updateDragPlane 拖动模式（true:拖动范围为以相机为中心的曲面（近似球面）;false:拖动范围为垂直于视线的平面（默认））
     */
    public setInformation(updateDragPlane: boolean): void {
        this.updateDragPlane = updateDragPlane;
    }

    public update(target: TransformNode): void {
        if (this.updateCall) this.updateCall(target, false);
    }

    public extract() {
    }

    /**
     * 拖动中的回调函数
     * @param func (target:目标;manual:手动触发):void
     */
    public set onUpdate(func: IProgressCallback) {
        this.updateCall = func;
    }

    public setStart(target: TransformNode, eventData: IDragEventData): void {
        super.setStart(target, eventData);
        const wp = target.absolutePosition;
        this.startOffset.copyFrom(this.currentPosition).subtractInPlace(wp);

        if (this.updateDragPlane) return;
        const camera = this.getActiveCamera(target.getScene());
        if (!camera) return;
        const cur = this.currentPosition;
        camera.getDirectionToRef(BabylonContainer.forward, this.camDirN);
        const plane = this.plane;
        Calculation.setPlaneFromPositionAndNormal(cur, this.camDirN, plane);

    }

    public setProgress(target: TransformNode, eventData: IDragProgressEventData): void {
        super.setProgress(target, eventData);
        const camera = this.getActiveCamera(target.getScene());
        if (!camera) return;
        const cur = this.currentPosition;
        const plane = this.plane;
        if (!this.updateDragPlane) {
            const camPos = this.camVec.copyFrom(camera.globalPosition);
            cur.projectOnPlaneToRef(plane, camPos, cur);
            if (this.updateCall) this.updateCall(target, true, eventData);
            return;
        }

        const pre = this.previousPosition;
        const camPos = this.camVec.copyFrom(camera.globalPosition);
        const dir = this.camDirN.copyFrom(camPos).subtractInPlace(pre);
        Calculation.setPlaneFromPositionAndNormal(pre, dir, plane);
        cur.projectOnPlaneToRef(plane, camPos, cur);
        if (this.updateCall) this.updateCall(target, true, eventData);
    }

}


export { DragNoneLimit };